//
//  FirstViewController.swift
//  MyLocations
//
//  Created by JokerAtBaoFeng on 16/3/15.
//  Copyright © 2016年 baofeng. All rights reserved.
//

import UIKit
import CoreLocation
import CoreData
import QuartzCore
import AudioToolbox

class CurrentLocationController: UIViewController, CLLocationManagerDelegate {
    
    
    //MARK: UI Controller Outlets Definitions
    @IBOutlet weak var messageLabel: UILabel!
    @IBOutlet weak var latitudeLabel: UILabel!
    @IBOutlet weak var latitudeTextLabel: UILabel!
    @IBOutlet weak var longitudeLabel: UILabel!
    @IBOutlet weak var longitudeTextLabel: UILabel!
    @IBOutlet weak var addressLabel: UILabel!
    @IBOutlet weak var tagButton: UIButton!
    @IBOutlet weak var getButton: UIButton!
    
    @IBOutlet weak var containerView: UIView!
    var logoVisible = false
    lazy var logoButton: UIButton = {
        let button = UIButton(type: .Custom) as UIButton
        
        button.setBackgroundImage(UIImage(named: "Logo"), forState: .Normal)
        button.sizeToFit()
        button.addTarget(self, action: Selector("getLocation"), forControlEvents: .TouchUpInside)
        
        button.center.x = CGRectGetMidX(self.view.bounds)
        button.center.y = 220
        
        return button
    }()
    
    //MARK: Other Data Variable Definitions
    let locationmanager = CLLocationManager()
    var location: CLLocation?
    
    var updateingLocation = false
    var lastLocationError: NSError?
    
    let geocoder = CLGeocoder()
    var placemark: CLPlacemark?
    var performingReverseGeocoding = false
    var lastGeocodingError: NSError?
    
    var timer: NSTimer?
    
    var managedObjectContext: NSManagedObjectContext?
    
    var soundID: SystemSoundID = 0
    
    //MARK: View Lift circle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        loadSoundEffect("Sound.caf")
        updateLabels()
        configureGetButton()
    }
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "TagLocation" {
            let navigatorController = segue.destinationViewController as! UINavigationController
            let controller = navigatorController.topViewController as! LocationDetailsViewController
            
            controller.coordinate = location!.coordinate
            controller.placemark = placemark
            controller.managedObjectContext = managedObjectContext
        }
    }
    
    //MARK: UI Action
    @IBAction func getLocation() {
        
        if logoVisible {
            hideLogoView()
        }
        
        let authStatus = CLLocationManager.authorizationStatus()
        
        if authStatus == .NotDetermined {
            locationmanager.requestWhenInUseAuthorization()
            return
        }
        
        if authStatus == .Denied || authStatus == .Restricted {
            showLocationServicesDeniedAlert()
            return
        }
        
        if updateingLocation{
            stopLocationManager()
        }else{
            location = nil
            lastLocationError = nil
            placemark = nil
            lastGeocodingError = nil
            startLocationManager()
        }
        updateLabels()
        configureGetButton()
        
    }
    
    //MARK: CoreLocation Service
    func startLocationManager()
    {
        if CLLocationManager.locationServicesEnabled() {
            locationmanager.delegate = self
            locationmanager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
            locationmanager.startUpdatingLocation()
            updateingLocation = true
            timer = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("didTimeOut"), userInfo: nil, repeats: false)
        }
    }
    
    func stopLocationManager()
    {
        if updateingLocation {
            locationmanager.stopUpdatingLocation()
            locationmanager.delegate = nil
            updateingLocation = false
            
            if let timer = timer{
                timer.invalidate()
            }
        }
    }
    func showLocationServicesDeniedAlert()
    {
        let alert = UIAlertController(title: "Location Services Disabled", message: "Please enable location service for this app in Settings.", preferredStyle: .Alert)
        
        let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
        alert.addAction(okAction)
        presentViewController(alert, animated: true, completion: nil)
    }
    
    func stringFromPlacemark(placemark: CLPlacemark) -> String{
        
        var line1: String = "";
        
        line1.addText(placemark.subThoroughfare)
        
        line1.addText(placemark.thoroughfare,withSeparator: " ")
        
        
        var line2: String = ""
        
        line2.addText(placemark.locality)
        
        line2.addText(placemark.administrativeArea, withSeparator: " " )
        
        line2.addText(placemark.postalCode, withSeparator: " ")
        
        if line1.isEmpty {
            return  line2 + "\n"
        }else{
            return line1 + "\n" + line2

        }
    }
    
    //MARK: CoreLocation Protocol functions
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        let newLocation = locations.last! as CLLocation
        print("didUpdateLocations\(newLocation)")
        
        if newLocation.timestamp .timeIntervalSinceNow < -5
        {
            return
        }
        
        if newLocation.horizontalAccuracy < 0 {
            return
        }
        
        var distance = CLLocationDistance(DBL_MAX)
        if let location = location {
            distance = newLocation.distanceFromLocation(location)
        }
        
        
        if location == nil || location!.horizontalAccuracy > newLocation.horizontalAccuracy{
            lastLocationError = nil
            location = newLocation
            updateLabels()
            
            
            if newLocation.horizontalAccuracy <= locationmanager.desiredAccuracy
            {
                print("*** We are done!")
                stopLocationManager()
                configureGetButton()
                
                if distance > 0 {
                    performingReverseGeocoding = false
                }
            }
            
            if !performingReverseGeocoding {
                print("*** Going to eocode")
                performingReverseGeocoding = true
                geocoder.reverseGeocodeLocation(location!, completionHandler: {
                    placemarks,error in
                    
                    print("***Found Placemark: \(placemarks), error: \(error?.description)")
                    
                    self.lastGeocodingError = error
                    if nil == error && !placemarks!.isEmpty {
                        if self.placemark == nil {
                            print("FIRST TIME")
                            self.playSoundEffect()
                        }
                        self.placemark = placemarks!.last! as CLPlacemark
                    }else{
                        self.placemark = nil
                    }
                    self.performingReverseGeocoding = false
                    self.updateLabels()
                })
            }
        }else if distance < 1.0 {
            let timeInterval = newLocation.timestamp.timeIntervalSinceDate(location!.timestamp)
            
            if timeInterval > 10 {
                print("*** Force done!")
                stopLocationManager()
                updateLabels()
                configureGetButton()
            }
            
        }
        
    }
    
    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("didFailedWithError\(error)")
        
        if error.code == CLError.LocationUnknown.rawValue {
            return
        }
        lastLocationError = error
        stopLocationManager()
        updateLabels()
        configureGetButton()
    }
    //MARK: Utilities
    
    func showLogoView()
    {
        if !logoVisible {
            logoVisible = true
            
            containerView.hidden = true
            view.addSubview(logoButton)
        }
    }
    
    func hideLogoView()
    {
        
        if !logoVisible {
            return
        }
        
        
        logoVisible = false
        containerView.hidden = false
        
        containerView.center.x = view.bounds.size.width * 2
        containerView.center.y = 40 + containerView.bounds.size.height / 2
        
        let centerX = CGRectGetMidX(view.bounds)
        
        let panelMover = CABasicAnimation(keyPath: "position")
        
        panelMover.removedOnCompletion = false
        panelMover.fillMode = kCAFillModeForwards
        panelMover.duration = 0.6
        panelMover.fromValue = NSValue(CGPoint: containerView.center)
        panelMover.toValue = NSValue(CGPoint: CGPoint(x: centerX, y: containerView.center.y))
        panelMover.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
        panelMover.delegate = self
        
        containerView.layer.addAnimation(panelMover, forKey: "panelMover")
        
        let logoMover = CABasicAnimation(keyPath: "position")
        logoMover.removedOnCompletion = false
        logoMover.fillMode = kCAFillModeForwards
        logoMover.duration = 0.5
        logoMover.fromValue = NSValue(CGPoint: logoButton.center)
        logoMover.toValue = NSValue(CGPoint: CGPoint(x: -centerX, y: logoButton.center.y))
        logoMover.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
        logoButton.layer.addAnimation(logoMover, forKey: "logoMover")
        
        let logoRotator = CABasicAnimation(keyPath: "transform.ratation.z")
        logoRotator.removedOnCompletion = false
        logoRotator.fillMode = kCAFillModeForwards
        logoRotator.duration = 0.5
        logoRotator.fromValue = 0.5
        logoRotator.toValue = -2 * M_PI
        logoRotator.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
        
        logoButton.layer.addAnimation(logoRotator, forKey: "logoRotator")
        
    }
    override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
        containerView.layer.removeAllAnimations()
        containerView.center.x = view.bounds.size.width / 2
        containerView.center.y = 40 + containerView.bounds.size.height / 2
        
        logoButton.layer.removeAllAnimations()
        logoButton.removeFromSuperview()
    }
    func updateLabels()
    {
        if let location = location {
            latitudeLabel.text = String(format: "%.8f", location.coordinate.latitude)
            longitudeLabel.text = String(format: "%.8f",location.coordinate.longitude)
            
            tagButton.hidden = false
            messageLabel.text = ""
            
            if let placemark = placemark {
                addressLabel.text = stringFromPlacemark(placemark)
            }else if performingReverseGeocoding {
                addressLabel.text = "Searching for address ..."
            }else if lastGeocodingError != nil {
                addressLabel.text = "Error Finding Address"
            }else{
                addressLabel.text = "No Address Found"
            }
            
            latitudeTextLabel.hidden = false
            longitudeTextLabel.hidden = false
            
        }else
        {
            latitudeLabel.text = ""
            longitudeLabel.text = ""
            addressLabel.text = ""
            tagButton.hidden = true
            
            var statusMessage: String
            if let error = lastLocationError {
                if error.domain == kCLErrorDomain &&
                    error.code == CLError.Denied.rawValue {
                        statusMessage = "Location Service Disabled"
                }else {
                    statusMessage = "Error Getting Location"
                }
            }else if !CLLocationManager.locationServicesEnabled(){
                statusMessage = "Location Services Disabled"
            }
            else if updateingLocation {
                statusMessage = "Searching ..."
            }else {
//                statusMessage = "Tap 'Get My Location' to Start"
                statusMessage = ""
                showLogoView()
            }
            messageLabel.text = statusMessage
            
            latitudeTextLabel.hidden = true
            longitudeTextLabel.hidden = true
            
        }
    }
    
    func configureGetButton()
    {
        let spinnerTag = 1000
        
        
        if updateingLocation {
            getButton.setTitle("Stop", forState: .Normal)
            
            if view.viewWithTag(spinnerTag) == nil {
                let spinner = UIActivityIndicatorView(activityIndicatorStyle: .White)
                
                spinner.center = messageLabel.center
                spinner.center.y += spinner.bounds.size.height / 2 + 15
                spinner.startAnimating()
                spinner.tag = spinnerTag
                containerView.addSubview(spinner)
            }
            
        }else{
            getButton.setTitle("Get My Location", forState: .Normal)
            
            if let spinner = containerView.viewWithTag(spinnerTag) {
                spinner.removeFromSuperview()
            }
        }
    }
    
    func didTimeOut()
    {
        print("*** Time Out");
        if location == nil {
            stopLocationManager()
            
            lastLocationError = NSError(domain: "MyLocationErrorDomain", code: 1, userInfo: nil)
            
            updateLabels()
            configureGetButton()
        }
    }
    
    
    func loadSoundEffect(name: String)
    {
        if let path = NSBundle.mainBundle().pathForResource(name, ofType: nil){
            let fileURL: NSURL? = NSURL.fileURLWithPath(path,isDirectory: false)
            if fileURL == nil {
                print("NSURL is nil for path: \(path)")
                return
            }
            
            let error = AudioServicesCreateSystemSoundID(fileURL!, &soundID)
            if error != kAudioServicesNoError {
                print("Error code \(error) loading sound at path: \(path)")
                return
            }
        }
    }
    
    func unloadSoundEffec(){
        AudioServicesDisposeSystemSoundID(soundID)
        soundID = 0
    }
    
    func playSoundEffect()
    {
        AudioServicesPlaySystemSound(soundID)
    }
}

